////////////////////////////////////////////////////
// Name:        variantref.h
// Purpose:     Base classes for reference-counted variant types
// Author:      Julian Smart
// Modified by:
// Created:     2006-05-11
// RCS-ID:      $$
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
////////////////////////////////////////////////////

#include "wx/wx.h"

#include "variantutils/variantref.h"

/*
 * wxVariantRefData is a wxVariantData object that is ref counted (holds and shares
 * the actual data). It doesn't get set directly into a wxVariant, but is stored
 * by a wxRefCountingVariantData object.
 */

IMPLEMENT_CLASS(wxVariantRefData, wxVariantData)

/*
 * wxRefCountingVariantData is a wxVariantData object that can do ref counting (points to a wxVariantRefData)
 */

IMPLEMENT_CLASS(wxRefCountingVariantData, wxVariantData)

// Copy to data
void wxRefCountingVariantData::Copy(wxVariantData& data)
{
    wxASSERT( data.IsKindOf(CLASSINFO(wxRefCountingVariantData)) );
    
    if (!data.IsKindOf(CLASSINFO(wxRefCountingVariantData)))
        return;

    wxRefCountingVariantData& refCountingData = (wxRefCountingVariantData&) data;

    refCountingData.Ref(*this);
}

bool wxRefCountingVariantData::Eq(wxVariantData& data) const
{
    wxASSERT( data.IsKindOf(CLASSINFO(wxRefCountingVariantData)) );
    
    if (!data.IsKindOf(CLASSINFO(wxRefCountingVariantData)))
        return false;

    wxRefCountingVariantData& refCountingData = (wxRefCountingVariantData&) data;

    return m_variantRefData == refCountingData.m_variantRefData;
}

bool wxRefCountingVariantData::Write(wxString& str) const
{
    if (m_variantRefData)
        return m_variantRefData->Write(str);
    else
        return false;
}

bool wxRefCountingVariantData::Read(wxString& str)
{
    if (m_variantRefData)
        return m_variantRefData->Read(str);
    else
        return false;
}

void wxRefCountingVariantData::Ref(const wxRefCountingVariantData& data)
{
    if (m_variantRefData == data.m_variantRefData)
        return;

    Unref();

    if (data.m_variantRefData)
    {
        m_variantRefData = data.m_variantRefData;
        m_variantRefData->Inc();
    }
}

void wxRefCountingVariantData::Unref()
{
    if (m_variantRefData)
    {
        wxASSERT_MSG( m_variantRefData->GetCount() > 0, _T("invalid ref data count") );

        m_variantRefData->Dec();
        if ( m_variantRefData->GetCount() == 0 )
            delete m_variantRefData;
        m_variantRefData = NULL;
    }
}

wxVariantRefData* wxRefCountingVariantData::CloneVariantRefData(const wxVariantRefData* refData)
{
    wxVariantRefData* newRefData = CreateVariantRefData();
    ((wxVariantRefData*) refData)->Copy(*newRefData);

    return newRefData;
}

// Make sure we're the exclusive owner of the data
void wxRefCountingVariantData::AllocExclusive()
{
    if (!m_variantRefData)
    {
        m_variantRefData = CreateVariantRefData();
        wxASSERT( m_variantRefData != NULL );
    }
    else if (m_variantRefData->GetCount() > 1)
    {
        // note that ref is not going to be destroyed in this case
        const wxVariantRefData* ref = m_variantRefData;
        UnRef();

        // ... so we can still access it
        m_variantRefData = CloneVariantRefData(ref);
    }
    //else: ref count is 1, we are exclusive owners of m_variantRefData anyhow
}

IMPLEMENT_CLASS(wxClonableVariantData, wxVariantData)

// wxWidgets is missing AllocExclusive for wxVariant
void wxVariantAllocExclusive(wxVariant& variant)
{
    if (variant.IsNull())
        return;

    if (variant.GetData()->GetRefCount() > 1)
    {
        wxClonableVariantData* data = wxDynamicCast(variant.GetData(), wxClonableVariantData);
        if (data)
            variant.SetData(data->Clone());
        else
        {
            wxString type = variant.GetType();

            // Do it type by type
            if (type == wxT("string"))
            {
                wxString value = variant.GetString();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("long"))
            {
                long value = variant.GetLong();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("bool"))
            {
                bool value = variant.GetBool();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("double"))
            {
                double value = variant.GetDouble();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("char"))
            {
                wxChar value = variant.GetChar();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("void*"))
            {
                void* value = variant.GetVoidPtr();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("datetime"))
            {
                wxDateTime value = variant.GetDateTime();
                variant.UnRef();
                variant = value;
            }
            else if (type == wxT("arrstring"))
            {
                wxArrayString value = variant.GetArrayString();
                variant.UnRef();
                variant = value;
            }
            else
            {
                wxString msg(wxString::Format(wxT("wxVariantAllocExclusive not implemented for type %s"), type.c_str()));
                wxASSERT_MSG(false, msg);
            }
        }
    }
    //else: ref count is 1, we are exclusive owners of m_variantRefData anyhow
}
